/**
 * 
 */
package lu.med.praxiswahl.wahlumed.client.utilities;

import java.io.*;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.w3c.dom.Node ;

/**
 * @author PatrickPierre
 *
 */
public class MSWordUtil { 
    
    private XWPFDocument document = null; 
    
    public MSWordUtil() { 
    } 
    
    public final void openFile(String filename) throws IOException { 
        File file = null; 
        FileInputStream fis = null; 
        try { 
            file = new File(filename); 
            fis = new FileInputStream(file); 
            this.document = new XWPFDocument(fis); 
        } 
        finally { 
            try { 
                if(fis != null) { 
                    fis.close(); 
                    fis = null; 
                } 
            } 
            catch(IOException ioEx) { 
                // Swallow this exception. It would have occured onyl 
                // when releasing the file handle and should not pose 
                // problems to later processing. 
            } 
        } 
    } 
    
    public final void saveAs(String filename) throws IOException { 
        File file = null; 
        FileOutputStream fos = null; 
        try { 
            file = new File(filename); 
            fos = new FileOutputStream(file); 
            this.document.write(fos); 
        } 
        finally { 
            if(fos != null) { 
                fos.close(); 
               fos = null; 
            } 
        } 
    } 
    
    public final void insertAtBookmark(String bookmarkName, String bookmarkValue) { 
        List<XWPFParagraph> paraList = null; 
        Iterator<XWPFParagraph> paraIter = null; 
        XWPFParagraph para = null; 
        List<CTBookmark> bookmarkList = null; 
        Iterator<CTBookmark> bookmarkIter = null; 
        CTBookmark bookmark = null; 
        XWPFRun run = null; 
        
        paraList = this.document.getParagraphs(); 
        paraIter = paraList.iterator(); 
            
        while(paraIter.hasNext()) { 
            para = paraIter.next(); 
                
            bookmarkList = para.getCTP().getBookmarkStartList(); 
            bookmarkIter = bookmarkList.iterator(); 
                
            while(bookmarkIter.hasNext()) { 
                bookmark = bookmarkIter.next(); 
                
                if(bookmark.getName().equals(bookmarkName)) { 
                	//System.out.println("Para::"+para.getParagraphText());
                    run = para.createRun(); 
                    run.setText(bookmarkValue);
                    
                    this.replaceBookmark(bookmark, run, para);
                } 
            } 
        } 
    } 
    
    private void replaceBookmark(CTBookmark bookmark, XWPFRun run, 
            XWPFParagraph para) { 
        Node nextNode = null; 
        Node styleNode = null; 
        Node lastRunNode = null; 
        Stack<Node> nodeStack = null; 
        int bookmarkStartID = 0; 
        int bookmarkEndID = -1; 

        nodeStack = new Stack<Node>(); 
        bookmarkStartID = bookmark.getId().intValue(); 
        nextNode = bookmark.getDomNode(); 

        // Loop through the nodes looking for a matching bookmarkEnd tag 
        while (bookmarkStartID != bookmarkEndID) { 

            nextNode = nextNode.getNextSibling(); 

            // If an end tag is found, does it match the start tag? If so, end 
            // the while loop. 
            if (nextNode.getNodeName().contains("bookmarkEnd")) { 
                try { 
                    bookmarkEndID = Integer.parseInt( 
                            nextNode.getAttributes().getNamedItem("w:id").getNodeValue()); 
                } catch (NumberFormatException nfe) { 
                    bookmarkEndID = bookmarkStartID; 
                } 
            } else { 
                // If this is not a bookmark end tag, store the reference to the 
                // node on the stack for later deletion. This is easier that 
                // trying to delete the nodes as they are found. 
                nodeStack.push(nextNode); 
            } 
        } 

        // If the stack of nodes found between the bookmark tags is not empty 
        // then they have to be removed. 
        if (!nodeStack.isEmpty()) { 

            // Check the node at the top of the stack. If it is a run, get it's 
            // style - if any - and apply to the run that will be replacing it. 
            lastRunNode = nodeStack.pop(); 
            if ((lastRunNode.getNodeName().equals("w:r"))) { 
                styleNode = this.getStyleNode(lastRunNode); 
                if (styleNode != null) { 
                    run.getCTR().getDomNode().insertBefore( 
                            styleNode.cloneNode(true), run.getCTR().getDomNode().getFirstChild()); 
                } 
            } 

            // Delete any and all node that were found in between the start and 
            // end tags. This is slightly safer that trying to delete the nodes 
            // as they are found wile stepping through them in the loop above. 
            para.getCTP().getDomNode().removeChild(lastRunNode); 
            // Now, delete the remaing Nodes on the stack 
            while (!nodeStack.isEmpty()) { 
                para.getCTP().getDomNode().removeChild(nodeStack.pop()); 
            } 
        } 

        // Place the text into position, between the bookmark tags. 
        para.getCTP().getDomNode().insertBefore( 
                run.getCTR().getDomNode(), nextNode); 
    } 

    
    private Node getStyleNode(Node parentNode) { 
        Node childNode = null; 
        Node styleNode = null; 
        if (parentNode != null) { 

            // If the node represents a run and it has child nodes then 
            // it can be processed further. Note, whilst testing the code, it 
            // was observed that although it is possible to get a list of a nodes 
            // children, even when a node did have children, trying to obtain this 
            // list would often return a null value. This is the reason why the 
            // technique of stepping from one node to the next is used here. 
            if (parentNode.getNodeName().equalsIgnoreCase("w:r") 
                    && parentNode.hasChildNodes()) { 

                // Get the first node and catch it's reference for return if 
                // the first child node is a style node (w:rPr). 
                childNode = parentNode.getFirstChild(); 
                if (childNode.getNodeName().equals("w:rPr")) { 
                    styleNode = childNode; 
                } else { 
                    // If the first node was not a style node and there are other 
                    // child nodes remaining to be checked, then step through 
                    // the remaining child nodes until either a style node is 
                    // found or until all child nodes have been processed. 
                    while ((childNode = childNode.getNextSibling()) != null) { 
                        if (childNode.getNodeName().equals("w:rPr")) { 
                            styleNode = childNode; 
                            // Note setting to null here if a style node is 
                            // found in order order to terminate any further 
                            // checking 
                            childNode = null; 
                        } 
                    } 
                } 
            } 
        } 
        return (styleNode); 
    } 
    
    
    public static void main(String[] args) { 
        try { 
            MSWordUtil docxTest = new MSWordUtil(); 
            docxTest.openFile("D:/Arztbrief.docx"); 
//            docxTest.insertAtBookmark("WHOLE_WORD", "This should be inserted at the WHOLE_WORD bookmark."); 
//            docxTest.insertAtBookmark("MARK_ONE", "..and this at the MARK_ONE bookmark."); 
            
            docxTest.insertAtBookmark("DOCTORNAME", "This should be inserted at the WHOLE_WORD bookmark.");
            docxTest.insertAtBookmark("DOCTORSPECIALITY", "..and this at the MARK_ONE bookmark.");
            docxTest.insertAtBookmark("DOCTORADRESS", "This should be inserted at the WHOLE_WORD bookmark.");
            docxTest.insertAtBookmark("PATIENTNAME", "..and this at the MARK_ONE bookmark.");
            docxTest.insertAtBookmark("PATIENTBIRTHDATE","This should be inserted at the WHOLE_WORD bookmark.");
			
            docxTest.saveAs("D:/Arztbrief_1.docx"); 
        } 
        catch(Exception ex) { 
            System.out.println("Caught a: " + ex.getClass().getName()); 
            System.out.println("Message: " + ex.getMessage()); 
            System.out.println("Stacktrace follows:....."); 
            ex.printStackTrace(System.out); 
        } 
    } 
} 
